-
Notifications
You must be signed in to change notification settings - Fork 0
events-rsvp get all rsvps for an event route created #427
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds RSVP functionality: new Fastify plugin exposing POST and GET RSVP endpoints (dev-registered), a new DynamoDB table with GSI and replicas, config and role additions, a Zod RSVP schema, and unit tests for POST and GET flows. (47 words) Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API as API (rsvpRoutes)
participant Auth as Auth Module
participant Membership as Membership Service
participant DynamoDB
rect rgb(235,245,255)
Note over Client,API: POST /:orgId/event/:eventId
Client->>API: POST with UIUC token (Authorization header)
API->>Auth: verifyUiucAccessToken(token)
Auth-->>API: principalName / upn
API->>API: derive netId from upn
API->>Membership: checkPaidMembership(netId, orgId)
Membership->>DynamoDB: lookup membership (Dynamo/Redis)
DynamoDB-->>Membership: membership flag
Membership-->>API: isPaidMember
API->>DynamoDB: PutItem into RSVPDynamoTableName
DynamoDB-->>API: PutItem success
API-->>Client: 201 Created (rsvp item)
end
rect rgb(250,250,230)
Note over Client,API: GET /:orgId/event/:eventId
Client->>API: GET (requires VIEW_RSVPS role)
API->>DynamoDB: Query EventIdIndex for eventId
DynamoDB-->>API: Items
API->>API: Unmarshal, dedupe by userId
API-->>Client: 200 OK (rsvp list)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
💰 Infracost reportMonthly estimate increased by $6 📈
*Usage costs were estimated using infracost-usage.yml, see docs for other options. Estimate details |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (1)
src/api/routes/rsvp.ts (1)
3-3: Unused imports:withRolesandgetUserOrgRoles.These imports are not currently used. If authorization is added per the previous comment, they will be needed. Otherwise, remove them.
Also applies to: 6-6
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
📒 Files selected for processing (3)
src/api/index.ts(2 hunks)src/api/routes/rsvp.ts(1 hunks)terraform/modules/dynamo/main.tf(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/api/routes/rsvp.ts (5)
src/api/components/index.ts (1)
withTags(243-258)src/api/functions/uin.ts (1)
verifyUiucAccessToken(30-96)src/common/errors/index.ts (1)
ValidationError(99-108)src/api/functions/membership.ts (1)
checkPaidMembership(359-400)src/common/config.ts (1)
genericConfig(244-244)
🪛 ESLint
src/api/routes/rsvp.ts
[error] 1-1: Resolve error: EACCES: permission denied, open '/pMkMEsaHQW'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:233:12)
at checkFileExtension (/home/jailuser/git/node_modules/eslint-plugin-import/lib/rules/extensions.js:205:53)
at checkSourceValue (/home/jailuser/git/node_modules/eslint-module-utils/moduleVisitor.js:32:5)
(import/extensions)
[error] 2-2: Unexpected use of file extension "js" for "api/plugins/rateLimiter.js"
(import/extensions)
[error] 3-3: Unexpected use of file extension "js" for "api/components/index.js"
(import/extensions)
[error] 6-6: Unexpected use of file extension "js" for "api/functions/organizations.js"
(import/extensions)
[error] 11-11: Unexpected use of file extension "js" for "common/errors/index.js"
(import/extensions)
[error] 13-13: Unexpected use of file extension "js" for "api/functions/uin.js"
(import/extensions)
[error] 14-14: Unexpected use of file extension "js" for "api/functions/membership.js"
(import/extensions)
[error] 16-16: Unexpected use of file extension "js" for "common/config.js"
(import/extensions)
src/api/index.ts
[error] 65-65: Unexpected use of file extension "js" for "./routes/rsvp.js"
(import/extensions)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build Application
🔇 Additional comments (1)
src/api/index.ts (1)
65-65: LGTM!The import and conditional registration of
rsvpRoutesfollows the existing patterns in the codebase. The dev-only gate is consistent with howvendingPluginis registered.Also applies to: 381-383
| "x-uiuc-token": z.jwt().min(1).meta({ | ||
| description: | ||
| "An access token for the user in the UIUC Entra ID tenant.", | ||
| }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Script executed:
#!/bin/bash
# Search for z.jwt() usage and any custom JWT schema definitions
rg -n "z\.jwt|\.jwt\(\)" --type ts -C 3Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
src/api/routes/syncIdentity.ts-64- {
src/api/routes/syncIdentity.ts-65- schema: withTags(["Generic"], {
src/api/routes/syncIdentity.ts-66- headers: z.object({
src/api/routes/syncIdentity.ts:67: "x-uiuc-token": z.jwt().min(1).meta({
src/api/routes/syncIdentity.ts-68- description:
src/api/routes/syncIdentity.ts-69- "An access token for the user in the UIUC Entra ID tenant.",
src/api/routes/syncIdentity.ts-70- }),
--
src/api/routes/syncIdentity.ts-150- {
src/api/routes/syncIdentity.ts-151- schema: withTags(["Generic"], {
src/api/routes/syncIdentity.ts-152- headers: z.object({
src/api/routes/syncIdentity.ts:153: "x-uiuc-token": z.jwt().min(1).meta({
src/api/routes/syncIdentity.ts-154- description:
src/api/routes/syncIdentity.ts-155- "An access token for the user in the UIUC Entra ID tenant.",
src/api/routes/syncIdentity.ts-156- }),
--
src/api/routes/rsvp.ts-40- }),
src/api/routes/rsvp.ts-41- }),
src/api/routes/rsvp.ts-42- headers: z.object({
src/api/routes/rsvp.ts:43: "x-uiuc-token": z.jwt().min(1).meta({
src/api/routes/rsvp.ts-44- description:
src/api/routes/rsvp.ts-45- "An access token for the user in the UIUC Entra ID tenant.",
src/api/routes/rsvp.ts-46- }),
--
src/api/routes/rsvp.ts-92- }),
src/api/routes/rsvp.ts-93- }),
src/api/routes/rsvp.ts-94- headers: z.object({
src/api/routes/rsvp.ts:95: "x-uiuc-token": z.jwt().min(1).meta({
src/api/routes/rsvp.ts-96- description:
src/api/routes/rsvp.ts-97- "An access token for the user in the UIUC Entra ID tenant.",
src/api/routes/rsvp.ts-98- }),
--
src/api/routes/v2/mobileWallet.ts-27- schema: withTags(["Mobile Wallet"], {
src/api/routes/v2/mobileWallet.ts-28- summary: "Retrieve mobile wallet pass for ACM member.",
src/api/routes/v2/mobileWallet.ts-29- headers: z.object({
src/api/routes/v2/mobileWallet.ts:30: "x-uiuc-token": z.jwt().min(1).meta({
src/api/routes/v2/mobileWallet.ts-31- description:
src/api/routes/v2/mobileWallet.ts-32- "An access token for the user in the UIUC Entra ID tenant.",
src/api/routes/v2/mobileWallet.ts-33- }),
--
src/api/routes/v2/membership.ts-77- {
src/api/routes/v2/membership.ts-78- schema: withTags(["Membership"], {
src/api/routes/v2/membership.ts-79- headers: z.object({
src/api/routes/v2/membership.ts:80: "x-uiuc-token": z.jwt().min(1).meta({
src/api/routes/v2/membership.ts-81- description:
src/api/routes/v2/membership.ts-82- "An access token for the user in the UIUC Entra ID tenant.",
src/api/routes/v2/membership.ts-83- }),
--
src/api/routes/membership.ts-45- {
src/api/routes/membership.ts-46- schema: withTags(["Membership"], {
src/api/routes/membership.ts-47- headers: z.object({
src/api/routes/membership.ts:48: "x-uiuc-token": z.jwt().min(1).meta({
src/api/routes/membership.ts-49- description:
src/api/routes/membership.ts-50- "An access token for the user in the UIUC Entra ID tenant.",
src/api/routes/membership.ts-51- }),
</shell_result>
Script executed:
#!/bin/bash
# Check for Zod version in package.json and any custom extensions
cat package.json | grep -A2 -B2 "zod"Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 1
</shell_result>
Script executed:
#!/bin/bash
# Read the specific file to see context around lines 43-46 and 95-98
head -100 src/api/routes/rsvp.ts | tail -60Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
}),
headers: z.object({
"x-uiuc-token": z.jwt().min(1).meta({
description:
"An access token for the user in the UIUC Entra ID tenant.",
}),
}),
}),
},
async (request, reply) => {
const accessToken = request.headers["x-uiuc-token"];
const verifiedData = await verifyUiucAccessToken({
accessToken,
logger: request.log,
});
const { userPrincipalName: upn, givenName, surname } = verifiedData;
const netId = upn.replace("@illinois.edu", "");
if (netId.includes("@")) {
request.log.error(
`Found UPN ${upn} which cannot be turned into NetID via simple replacement.`,
);
throw new ValidationError({
message: "ID token could not be parsed.",
});
}
const isPaidMember = await checkPaidMembership({
netId,
dynamoClient: fastify.dynamoClient,
redisClient: fastify.redisClient,
logger: request.log,
});
const entry = {
partitionKey: `${request.params.eventId}#${upn}`,
eventId: request.params.eventId,
userId: upn,
isPaidMember,
createdAt: "",
};
},
);
fastify.withTypeProvider<FastifyZodOpenApiTypeProvider>().get(
"/:orgId/event/:eventId",
{
schema: withTags(["RSVP"], {
summary: "Get all RSVPs for an event.",
params: z.object({
eventId: z.string().min(1).meta({
description: "The previously-created event ID in the events API.",
}),
orgId: z.string().min(1).meta({
description: "The organization ID the event belongs to.",
}),
}),
headers: z.object({
"x-uiuc-token": z.jwt().min(1).meta({
description:
"An access token for the user in the UIUC Entra ID tenant.",
}),
}),
}),
</shell_result>
Script executed:
#!/bin/bash
# Search for any zod extensions or custom schema definitions
rg -n "z\.|extend\(|.jwt" src/api/routes/rsvp.ts -C 2Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
16-import { genericConfig } from "common/config.js";
17-
18:const rsvpItemSchema = z.object({
19: eventId: z.string(),
20: userId: z.string(),
21: isPaidMember: z.boolean(),
22: createdAt: z.string(),
23-});
24:const rsvpListSchema = z.array(rsvpItemSchema);
25-
26-const rsvpRoutes: FastifyPluginAsync = async (fastify, _options) => {
--
35- schema: withTags(["RSVP"], {
36- summary: "Submit an RSVP for an event.",
37: params: z.object({
38: eventId: z.string().min(1).meta({
39- description: "The previously-created event ID in the events API.",
40- }),
41- }),
42: headers: z.object({
43: "x-uiuc-token": z.jwt().min(1).meta({
44- description:
45- "An access token for the user in the UIUC Entra ID tenant.",
--
84- schema: withTags(["RSVP"], {
85- summary: "Get all RSVPs for an event.",
86: params: z.object({
87: eventId: z.string().min(1).meta({
88- description: "The previously-created event ID in the events API.",
89- }),
90: orgId: z.string().min(1).meta({
91- description: "The organization ID the event belongs to.",
92- }),
93- }),
94: headers: z.object({
95: "x-uiuc-token": z.jwt().min(1).meta({
96- description:
97- "An access token for the user in the UIUC Entra ID tenant.",
--
111- const response = await fastify.dynamoClient.send(commnand);
112- const items = response.Items?.map((item) => unmarshall(item)) || [];
113: return reply.send(items as z.infer<typeof rsvpListSchema>);
114- },
115- );
</shell_result>
Script executed:
#!/bin/bash
# Search for jwt function definition or Zod extension
rg -n "jwt\s*[:=]|\.jwt\s*=|extend.*jwt" --type tsRepository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 1
</shell_result>
Script executed:
#!/bin/bash
# Check imports in rsvp.ts to understand where z comes from
head -20 src/api/routes/rsvp.tsRepository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
import { FastifyPluginAsync } from "fastify";
import rateLimiter from "api/plugins/rateLimiter.js";
import { withRoles, withTags } from "api/components/index.js";
import { QueryCommand } from "@aws-sdk/client-dynamodb";
import { unmarshall } from "@aws-sdk/util-dynamodb";
import { getUserOrgRoles } from "api/functions/organizations.js";
import {
UnauthenticatedError,
UnauthorizedError,
ValidationError,
} from "common/errors/index.js";
import * as z from "zod/v4";
import { verifyUiucAccessToken } from "api/functions/uin.js";
import { checkPaidMembership } from "api/functions/membership.js";
import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
import { genericConfig } from "common/config.js";
const rsvpItemSchema = z.object({
eventId: z.string(),
userId: z.string(),
</shell_result>
Script executed:
#!/bin/bash
# Look for zod configuration or extensions files
fd -e ts -e js | xargs rg -l "z\.extend|ZodType.*jwt" | head -20Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
</shell_result>
Script executed:
#!/bin/bash
# Check package.json for zod version and look at dependencies
cat package.json | head -50Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
{
"name": "infra-core",
"version": "1.0.0",
"private": true,
"type": "module",
"workspaces": [
"src/api",
"src/ui",
"src/archival",
"src/linkryEdgeFunction"
],
"packageManager": "yarn@1.22.22",
"scripts": {
"postinstall": "npm run setup",
"setup": "git config blame.ignoreRevsFile .git-blame-ignore-revs",
"build": "concurrently --names 'api,ui,archival,linkryEdge' 'yarn workspace infra-core-api run build' 'yarn workspace infra-core-ui run build' 'yarn workspace infra-core-archival run build' 'yarn workspace infra-core-linkry-edge run build'",
"postbuild": "node src/api/createLambdaPackage.js && yarn lockfile-manage",
"dev": "cross-env DISABLE_AUDIT_LOG=true concurrently --names 'api,ui' 'yarn workspace infra-core-api run dev' 'yarn workspace infra-core-ui run dev'",
"lockfile-manage": "cp src/api/package.lambda.json dist/lambda/package.json && cp src/api/package.lambda.json dist/sqsConsumer/package.json && cp yarn.lock dist/lambda/ && cp yarn.lock dist/sqsConsumer/",
"prettier": "yarn workspaces run prettier && prettier --check tests/**/*.ts",
"prettier:write": "yarn workspaces run prettier:write && prettier --write tests/**/*.ts",
"lint": "yarn workspaces run lint",
"prepare": "node .husky/install.mjs || true",
"typecheck": "yarn workspaces run typecheck",
"test:unit": "cross-env RunEnvironment='dev' concurrently --names 'api,ui' 'yarn run test:unit-api' 'yarn workspace infra-core-ui run test:unit'",
"test:unit-api": "cross-env RunEnvironment='dev' vitest run --coverage --config tests/unit/vitest.config.ts tests/unit",
"test:unit-ui": "yarn test:unit --ui",
"test:unit-watch": "vitest tests/unit",
"test:live": "vitest tests/live",
"test:live-ui": "yarn test:live --ui",
"test:e2e": "playwright test",
"test:e2e-ui": "playwright test --ui"
},
"dependencies": {
"@acm-uiuc/js-shared": "^3.2.1"
},
"devDependencies": {
"@eslint/compat": "^1.3.2",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.33.0",
"@playwright/test": "^1.56.1",
"@smithy/types": "^4.5.0",
"@tsconfig/node22": "^22.0.1",
"@types/ioredis-mock": "^8.2.5",
"@types/node": "^24.3.0",
"@types/pluralize": "^0.0.33",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@types/supertest": "^6.0.3",
"@typescript-eslint/eslint-plugin": "^8.40.0",
</shell_result>
Script executed:
#!/bin/bash
# Search for any z.jwt or jwt extension setup
rg -n "jwt" --type ts -g "!node_modules" | grep -i "extend\|z\.jwt\|define" | head -20Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
src/api/routes/rsvp.ts:43: "x-uiuc-token": z.jwt().min(1).meta({
src/api/routes/rsvp.ts:95: "x-uiuc-token": z.jwt().min(1).meta({
src/api/routes/v2/membership.ts:80: "x-uiuc-token": z.jwt().min(1).meta({
src/api/routes/v2/mobileWallet.ts:30: "x-uiuc-token": z.jwt().min(1).meta({
src/api/routes/syncIdentity.ts:67: "x-uiuc-token": z.jwt().min(1).meta({
src/api/routes/syncIdentity.ts:153: "x-uiuc-token": z.jwt().min(1).meta({
src/api/routes/membership.ts:48: "x-uiuc-token": z.jwt().min(1).meta({
</shell_result>
Script executed:
#!/bin/bash
# Look for zod setup or configuration files
fd -type f \( -name "*zod*" -o -name "*config*" \) --type ts --type js | head -20Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stderr:
error: 'ype' isn't a valid value for '--type <filetype>'
[possible values: file, directory, symlink, executable, empty, socket, pipe]
For more information try '--help'
</shell_result>
Script executed:
#!/bin/bash
# Search in common directory for any Zod extensions
rg -n "z\.|jwt" src/common --type ts -C 2 | head -50Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
src/common/utils.ts-38-
src/common/utils.ts-39-
src/common/utils.ts:40:export const nonEmptyCommaSeparatedStringSchema = z.
src/common/utils.ts:41: array(z.string().min(1)).
src/common/utils.ts-42- min(1, { message: "Filter expression must select at least one item." }).
src/common/utils.ts-43- transform((val) => val.map((item) => item.trim()))
--
src/common/utils.ts-48-export const getDefaultFilteringQuerystring = ({ defaultSelect }: GetDefaultFilteringQuerystringInput) => {
src/common/utils.ts-49- return {
src/common/utils.ts:50: select: z.optional(nonEmptyCommaSeparatedStringSchema).default(defaultSelect).meta({
src/common/utils.ts-51- description: "A list of attributes to return.",
src/common/utils.ts-52- ...(defaultSelect.length === 0 ? { default: ["<ALL ATTRIBUTES>"] } : { example: defaultSelect })
--
src/common/types/user.ts-2-import { illinoisUin } from "./generic.js";
src/common/types/user.ts-3-
src/common/types/user.ts:4:export const searchUserByUinRequest = z.object({
src/common/types/user.ts-5- uin: illinoisUin
src/common/types/user.ts-6-});
src/common/types/user.ts-7-
src/common/types/user.ts:8:export const searchUserByUinResponse = z.object({
src/common/types/user.ts:9: email: z.email(),
src/common/types/user.ts-10-});
--
src/common/types/tickets.ts-1-import * as z from "zod/v4";
src/common/types/tickets.ts:2:export const postMetadataSchema = z.object({
src/common/types/tickets.ts:3: type: z.union([z.literal("merch"), z.literal("ticket")]),
src/common/types/tickets.ts:4: itemSalesActive: z.union([z.date(), z.boolean()])
src/common/types/tickets.ts-5-});
src/common/types/tickets.ts-6-
src/common/types/tickets.ts:7:export type ItemPostData = z.infer<typeof postMetadataSchema>;
--
src/common/types/stripe.ts-1-import * as z from "zod/v4";
src/common/types/stripe.ts-2-
src/common/types/stripe.ts:3:export const invoiceLinkPostResponseSchema = z.object({
src/common/types/stripe.ts:4: id: z.string().min(1),
src/common/types/stripe.ts:5: link: z.string().url()
src/common/types/stripe.ts-6-});
src/common/types/stripe.ts-7-
src/common/types/stripe.ts:8:export const invoiceLinkPostRequestSchema = z.object({
src/common/types/stripe.ts:9: invoiceId: z.string().min(1),
src/common/types/stripe.ts:10: invoiceAmountUsd: z.number().min(50),
src/common/types/stripe.ts:11: contactName: z.string().min(1),
src/common/types/stripe.ts:12: contactEmail: z.string().email(),
src/common/types/stripe.ts:13: achPaymentsEnabled: z.optional(z.boolean()).default(false),
src/common/types/stripe.ts-14-});
src/common/types/stripe.ts-15-
src/common/types/stripe.ts:16:export type PostInvoiceLinkRequest = z.infer<
src/common/types/stripe.ts-17- typeof invoiceLinkPostRequestSchema>;
src/common/types/stripe.ts-18-
src/common/types/stripe.ts-19-
</shell_result>
Script executed:
#!/bin/bash
# Check API workspace for zod version
cat src/api/package.json | grep -A 2 -B 2 "zod"Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
"fastify-plugin": "^5.0.1",
"fastify-raw-body": "^5.0.0",
"fastify-zod-openapi": "^5.0.1",
"handlebars": "^4.7.8",
"ical-generator": "^10.0.0",
--
"stripe": "^19.2.0",
"uuid": "^13.0.0",
"zod": "^4.0.14",
"zod-validation-error": "^4.0.1"
},
"devDependencies": {
</shell_result>
Script executed:
#!/bin/bash
# Search for where jwt() might be extended onto z
rg -n "\.jwt\s*=" --type tsRepository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 1
</shell_result>
Script executed:
#!/bin/bash
# Look for any custom Zod types or extensions
rg -n "extend\|\.jwt\|jwt\(" --type ts | grep -v "x-uiuc-token" | head -30Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
</shell_result>
Script executed:
#!/bin/bash
# Check if zod has jwt method in v4
web_search "Zod v4 jwt method"Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 127
stderr:
/bin/bash: line 3: web_search: command not found
</shell_result>
Script executed:
#!/bin/bash
# Look for .d.ts files or type augmentation
find . -name "*.d.ts" -o -name "*types*" | grep -v node_modules | head -20Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
./src/api/resources/types.d.ts
./src/api/types.d.ts
./src/common/types
./src/ui/types.d.ts
./src/ui/vite-env.d.ts
</shell_result>
Script executed:
#!/bin/bash
# Search for any Zod augmentation or type declaration
rg -n "declare|augment|namespace.*z|interface.*z" --type ts | grep -i zod | head -20Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
</shell_result>
Script executed:
#!/bin/bash
# Check if there's a tsconfig that might reveal anything
cat tsconfig.json | head -50Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stderr:
cat: tsconfig.json: No such file or directory
</shell_result>
Script executed:
#!/bin/bash
# More thorough search for "jwt" across all files
rg -n "jwt" --type ts -C 1 | head -50Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
tests/unit/tickets.test.ts-31-const ddbMock = mockClient(DynamoDBClient);
tests/unit/tickets.test.ts:32:const jwt_secret = testSecretObject["jwt_key"];
tests/unit/tickets.test.ts:33:vi.stubEnv("JwtSigningKey", jwt_secret);
tests/unit/tickets.test.ts-34-
--
tests/unit/secret.testdata.ts-18-const testSecretObject = {
tests/unit/secret.testdata.ts:19: jwt_key: "somethingreallysecret",
tests/unit/secret.testdata.ts-20-};
--
tests/unit/secret.testdata.ts-28-
tests/unit/secret.testdata.ts:29:const jwtPayload = {
tests/unit/secret.testdata.ts:30: aud: "custom_jwt",
tests/unit/secret.testdata.ts:31: iss: "custom_jwt",
tests/unit/secret.testdata.ts-32- iat: Math.floor(Date.now() / 1000),
--
tests/unit/secret.testdata.ts-54-
tests/unit/secret.testdata.ts:55:const jwtPayloadNoGroups = {
tests/unit/secret.testdata.ts:56: aud: "custom_jwt",
tests/unit/secret.testdata.ts:57: iss: "custom_jwt",
tests/unit/secret.testdata.ts-58- iat: Math.floor(Date.now() / 1000),
--
tests/unit/secret.testdata.ts-85- testSecretObject,
tests/unit/secret.testdata.ts:86: jwtPayload,
tests/unit/secret.testdata.ts:87: jwtPayloadNoGroups,
tests/unit/secret.testdata.ts-88- uinSecretJson,
--
tests/unit/logs.test.ts-13-const ddbMock = mockClient(DynamoDBClient);
tests/unit/logs.test.ts:14:const jwt_secret = testSecretObject["jwt_key"];
tests/unit/logs.test.ts:15:vi.stubEnv("JwtSigningKey", jwt_secret);
tests/unit/logs.test.ts-16-
--
tests/unit/linkry.test.ts-17-const ddbMock = mockClient(DynamoDBClient);
tests/unit/linkry.test.ts:18:const jwt_secret = testSecretObject["jwt_key"];
tests/unit/linkry.test.ts:19:vi.stubEnv("JwtSigningKey", jwt_secret);
tests/unit/linkry.test.ts-20-
--
tests/unit/ical.test.ts-12-const ddbMock = mockClient(DynamoDBClient);
tests/unit/ical.test.ts:13:const jwt_secret = testSecretObject["jwt_key"];
tests/unit/ical.test.ts:14:vi.stubEnv("JwtSigningKey", jwt_secret);
tests/unit/ical.test.ts-15-
--
tests/unit/events.test.ts-15-const ddbMock = mockClient(DynamoDBClient);
tests/unit/events.test.ts:16:const jwt_secret = testSecretObject["jwt_key"];
tests/unit/events.test.ts:17:vi.stubEnv("JwtSigningKey", jwt_secret);
tests/unit/events.test.ts-18-
--
tests/unit/eventPost.test.ts-19-const ddbMock = mockClient(DynamoDBClient);
tests/unit/eventPost.test.ts:20:const jwt_secret = testSecretObject["jwt_key"];
tests/unit/eventPost.test.ts:21:vi.stubEnv("JwtSigningKey", jwt_secret);
tests/unit/eventPost.test.ts-22-
</shell_result>
Script executed:
#!/bin/bash
# Read the API types file which likely contains the jwt() extension
cat src/api/types.d.tsRepository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
/* eslint-disable @typescript-eslint/no-unused-vars */
import { FastifyRequest, FastifyInstance, FastifyReply } from "fastify";
import { AppRoles, RunEnvironment } from "../common/roles.js";
import { AadToken } from "./plugins/auth.js";
import { ConfigType, SecretConfig, SecretTesting } from "../common/config.js";
import NodeCache from "node-cache";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
import { SQSClient } from "@aws-sdk/client-sqs";
import { AvailableAuthorizationPolicy } from "common/policies/definition.js";
import type RedisModule from "ioredis";
import { type S3Client } from "@aws-sdk/client-s3";
export type Redis = RedisModule.default;
export type ValidLoggers = FastifyBaseLogger | pino.Logger;
interface CloudfrontLocation {
country: string | undefined;
city: string | undefined;
region: string | undefined;
latitude: string | undefined;
longitude: string | undefined;
postalCode: string | undefined;
}
declare module "fastify" {
interface FastifyInstance {
authenticate: (
request: FastifyRequest,
reply: FastifyReply,
) => Promise<void>;
authorize: (
request: FastifyRequest,
reply: FastifyReply,
validRoles: AppRoles[],
disableApiKeyAuth: boolean,
) => Promise<Set<AppRoles>>;
authorizeFromSchema: (
request: FastifyRequest,
reply: FastifyReply,
) => Promise<void>;
runEnvironment: RunEnvironment;
environmentConfig: ConfigType;
nodeCache: NodeCache;
dynamoClient: DynamoDBClient;
sqsClient?: SQSClient;
s3Client?: S3Client;
redisClient: Redis;
secretsManagerClient: SecretsManagerClient;
secretConfig: SecretConfig | (SecretConfig & SecretTesting);
refreshSecretConfig: CallableFunction;
}
interface FastifyRequest {
startTime: number;
username?: string;
userRoles?: Set<AppRoles>;
tokenPayload?: AadToken;
policyRestrictions?: AvailableAuthorizationPolicy[];
location: CloudfrontLocation;
}
}
export type NoDataRequest = {
Params: undefined;
Querystring: undefined;
Body: undefined;
};
</shell_result>
Script executed:
#!/bin/bash
# Also check the resources types
cat src/api/resources/types.d.tsRepository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
/* eslint-disable @typescript-eslint/no-explicit-any */
declare module "*.png" {
const value: string;
export default value;
}
declare module "*.json" {
const value: Record<string, any>;
export default value;
}
</shell_result>
Script executed:
#!/bin/bash
# Search for fastify-zod-openapi imports and usage
rg -n "fastify-zod-openapi" --type tsRepository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
src/api/components/index.ts:2:import { FastifyZodOpenApiSchema } from "fastify-zod-openapi";
src/api/index.ts:24:} from "fastify-zod-openapi";
src/api/routes/ics.ts:24:} from "fastify-zod-openapi";
src/api/routes/logs.ts:12:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/membership.ts:23:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/organizations.ts:12:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/mobileWallet.ts:15:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/rsvp.ts:15:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/roomRequests.ts:29:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/vending.ts:3:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/user.ts:16:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/syncIdentity.ts:8:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/tickets.ts:28:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/stripe.ts:42:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/iam.ts:46:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/linkry.ts:47:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/v2/mobileWallet.ts:12:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/v2/membership.ts:18:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/apiKey.ts:5:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/plugins/authorizeFromSchema.ts:4:import { FastifyZodOpenApiSchema } from "fastify-zod-openapi";
src/api/routes/events.ts:44:} from "fastify-zod-openapi";
</shell_result>
Script executed:
#!/bin/bash
# Check if there's any setup file that might extend Zod
rg -n "import.*z|from.*zod" src/api --type ts | head -20Repository: acm-uiuc/core
<shell_result>
Repository: acm-uiuc/core
Exit code: 0
stdout:
src/api/index.ts:24:} from "fastify-zod-openapi";
src/api/index.ts:25:import { type ZodOpenApiVersion } from "zod-openapi";
src/api/index.ts:39:import authorizeFromSchemaPlugin from "./plugins/authorizeFromSchema.js";
src/api/index.ts:44:import organizationsPlugin from "./routes/organizations.js";
src/api/types.d.ts:10:import { AvailableAuthorizationPolicy } from "common/policies/definition.js";
src/api/routes/mobileWallet.ts:6:import * as z from "zod/v4";
src/api/routes/mobileWallet.ts:15:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/logs.ts:12:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/logs.ts:13:import * as z from "zod/v4";
src/api/routes/tickets.ts:2:import * as z from "zod/v4";
src/api/routes/tickets.ts:28:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/membership.ts:23:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/membership.ts:24:import * as z from "zod/v4";
src/api/routes/vending.ts:3:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/vending.ts:4:import * as z from "zod/v4";
src/api/routes/rsvp.ts:6:import { getUserOrgRoles } from "api/functions/organizations.js";
src/api/routes/rsvp.ts:12:import * as z from "zod/v4";
src/api/routes/rsvp.ts:15:import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi";
src/api/routes/user.ts:4:import { getUserOrgRoles } from "api/functions/organizations.js";
src/api/routes/user.ts:10:import * as z from "zod/v4";
</shell_result>
Zod v4 jwt method
<web_search_result>
Short answer: Zod v4 adds a string validator for JWTs: use z.string().jwt() (optionally z.string().jwt({ alg: "RS256" }) to require a specific alg). It only checks the three-part/base64url structure (and decodes header/payload), it does NOT verify signatures — use a JWT library (e.g., jsonwebtoken or jose) for cryptographic verification. [1][2]
</web_search_result>
Fix z.jwt() to z.string().jwt() — z.jwt() is not a valid Zod v4 method.
Zod v4 provides z.string().jwt() to validate JWT strings, not z.jwt(). The current code will fail at runtime. Replace z.jwt().min(1) with z.string().jwt().min(1).
Note: z.string().jwt() validates the JWT structure (three-part base64url format) but does not verify signatures—use a JWT library like jose for cryptographic verification.
🤖 Prompt for AI Agents
In src/api/routes/rsvp.ts around lines 43-46, replace the invalid Zod call
z.jwt().min(1) with z.string().jwt().min(1) so the header schema uses
z.string().jwt().min(1) to validate JWT-formatted strings; ensure imports still
reference z from zod and keep min(1) in the chain (or move before jwt() if
preferred).
| const entry = { | ||
| partitionKey: `${request.params.eventId}#${upn}`, | ||
| eventId: request.params.eventId, | ||
| userId: upn, | ||
| isPaidMember, | ||
| createdAt: "", | ||
| }; | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
POST handler is incomplete — entry is created but never persisted or returned.
The handler constructs an RSVP entry object but:
- Never writes it to DynamoDB
- Never sends a response to the client
- Sets
createdAtto an empty string instead of a timestamp
The handler needs to persist the entry and return a response. Example fix:
const entry = {
partitionKey: `${request.params.eventId}#${upn}`,
eventId: request.params.eventId,
userId: upn,
isPaidMember,
- createdAt: "",
+ createdAt: new Date().toISOString(),
};
+ const putCommand = new PutItemCommand({
+ TableName: genericConfig.RsvpDynamoTableName, // Add this config
+ Item: marshall(entry),
+ });
+ await fastify.dynamoClient.send(putCommand);
+ return reply.status(201).send(entry);
},You'll also need to import PutItemCommand from @aws-sdk/client-dynamodb and marshall from @aws-sdk/util-dynamodb.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/api/routes/rsvp.ts around lines 72-79 the POST handler builds an RSVP
entry but never persists it or responds to the client and createdAt is empty;
fix by importing PutItemCommand from @aws-sdk/client-dynamodb and marshall from
@aws-sdk/util-dynamodb, set entry.createdAt = new Date().toISOString(), create a
PutItemCommand with TableName and Item: marshall(entry), await
dynamoDbClient.send(putCmd) (wrap in try/catch to handle errors), and finally
return an appropriate HTTP response (e.g., 201 with the saved entry or a
location header) or a 500 on failure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (2)
src/api/routes/rsvp.ts (2)
38-43: Fixz.jwt()toz.string().jwt().Zod v4 provides
z.string().jwt()for JWT validation, notz.jwt(). This will cause a runtime error.headers: z.object({ - "x-uiuc-token": z.jwt().min(1).meta({ + "x-uiuc-token": z.string().jwt().min(1).meta({ description: "An access token for the user in the UIUC Entra ID tenant.", }), }),
68-74: SetcreatedAtto actual timestamp.
createdAtis set to an empty string, which loses the timestamp information. Usenew Date().toISOString()for a proper ISO 8601 timestamp.const entry = { partitionKey: `${request.params.eventId}#${upn}`, eventId: request.params.eventId, userId: upn, isPaidMember, - createdAt: "", + createdAt: new Date().toISOString(), };
🧹 Nitpick comments (2)
tests/unit/rsvps.test.ts (1)
119-156: GET test looks good, but consider adding edge case coverage.The happy path is well tested. Consider adding tests for:
- Empty results (no RSVPs for event)
- Authorization failures (missing VIEW_RSVPS role)
- Invalid event ID
src/api/routes/rsvp.ts (1)
52-52: Unused destructured variables.
givenNameandsurnameare destructured but never used. Consider removing them or using them (e.g., for logging).- const { userPrincipalName: upn, givenName, surname } = verifiedData; + const { userPrincipalName: upn } = verifiedData;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (6)
src/api/routes/rsvp.ts(1 hunks)src/common/config.ts(2 hunks)src/common/roles.ts(1 hunks)src/common/types/rsvp.ts(1 hunks)terraform/modules/dynamo/main.tf(1 hunks)tests/unit/rsvps.test.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- terraform/modules/dynamo/main.tf
🧰 Additional context used
🧬 Code graph analysis (1)
src/api/routes/rsvp.ts (5)
src/api/components/index.ts (2)
withTags(243-258)withRoles(194-241)src/api/functions/uin.ts (1)
verifyUiucAccessToken(30-96)src/common/errors/index.ts (2)
ValidationError(99-108)DatabaseFetchError(121-130)src/api/functions/membership.ts (1)
checkPaidMembership(359-400)src/common/roles.ts (2)
AppRoles(35-35)AppRoles(36-36)
🪛 ESLint
tests/unit/rsvps.test.ts
[error] 1-1: Resolve error: EACCES: permission denied, open '/XtUyUAvhlL'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:233:12)
at checkFileExtension (/home/jailuser/git/node_modules/eslint-plugin-import/lib/rules/extensions.js:205:53)
at checkSourceValue (/home/jailuser/git/node_modules/eslint-module-utils/moduleVisitor.js:32:5)
(import/extensions)
[error] 9-9: Unexpected use of file extension "js" for "../../src/api/index.js"
(import/extensions)
[error] 10-10: Unexpected use of file extension "js" for "./auth.test.js"
(import/extensions)
[error] 11-11: Unexpected use of file extension "js" for "./secret.testdata.js"
(import/extensions)
[error] 12-12: Unexpected use of file extension "js" for "../../src/api/types.js"
(import/extensions)
[error] 74-74: ["jwt_key"] is better written in dot notation.
(dot-notation)
src/common/types/rsvp.ts
[error] 1-1: Resolve error: EACCES: permission denied, open '/txSPdyxJiv'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:233:12)
at checkFileExtension (/home/jailuser/git/node_modules/eslint-plugin-import/lib/rules/extensions.js:205:53)
at checkSourceValue (/home/jailuser/git/node_modules/eslint-module-utils/moduleVisitor.js:32:5)
(import/extensions)
src/api/routes/rsvp.ts
[error] 1-1: Resolve error: EACCES: permission denied, open '/MgMsEepBoK'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:233:12)
at checkFileExtension (/home/jailuser/git/node_modules/eslint-plugin-import/lib/rules/extensions.js:205:53)
at checkSourceValue (/home/jailuser/git/node_modules/eslint-module-utils/moduleVisitor.js:32:5)
(import/extensions)
[error] 2-2: Unexpected use of file extension "js" for "api/plugins/rateLimiter.js"
(import/extensions)
[error] 3-3: Unexpected use of file extension "js" for "api/components/index.js"
(import/extensions)
[error] 11-11: Unexpected use of file extension "js" for "common/errors/index.js"
(import/extensions)
[error] 13-13: Unexpected use of file extension "js" for "api/functions/uin.js"
(import/extensions)
[error] 14-14: Unexpected use of file extension "js" for "api/functions/membership.js"
(import/extensions)
[error] 16-16: Unexpected use of file extension "js" for "common/config.js"
(import/extensions)
[error] 17-17: Unexpected use of file extension "js" for "common/roles.js"
(import/extensions)
[error] 119-119: Insert ,
(prettier/prettier)
🪛 GitHub Actions: QA deploy - @MeghP89
src/api/routes/rsvp.ts
[error] 119-119: prettier/prettier: Insert ,
🪛 GitHub Check: Run Unit Tests
src/api/routes/rsvp.ts
[failure] 119-119:
Insert ,
🪛 Gitleaks (8.29.1)
tests/unit/rsvps.test.ts
[high] 16-16: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.
(jwt)
🔇 Additional comments (4)
src/common/config.ts (1)
41-41: LGTM!The
RSVPDynamoTableNameconfiguration is properly added to both the type definition and the config object, following the existing naming conventions.Also applies to: 88-88
tests/unit/rsvps.test.ts (2)
15-16: Test JWT constant is fine — Gitleaks flag is a false positive.This is a well-known example JWT from jwt.io documentation used purely for testing. It contains no sensitive data and is safe to use in test fixtures.
85-117: POST test looks good.The test correctly verifies the RSVP creation flow, including the response structure and DynamoDB interaction.
src/common/types/rsvp.ts (1)
1-8: I'll verify the concerns in this review comment by checking if the schema is actually unused and whether there's duplication in the codebase.
<function_calls>
shell
#!/bin/bashCheck if rsvpItemSchema from common/types/rsvp is imported anywhere
echo "=== Checking for imports from common/types/rsvp ==="
rg -n "from.*common/types/rsvp" --type tsecho ""
echo "=== Checking for all rsvpItemSchema definitions ==="
rg -n "rsvpItemSchema" --type ts -B2 -A2echo ""
echo "=== Checking for rsvpListSchema ==="
rg -n "rsvpListSchema" --type ts -B2 -A2
</function_calls>
| if (!response || !response.Items) { | ||
| throw new DatabaseFetchError({ | ||
| message: "Failed to get all member lists.", | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix misleading error message.
The error message says "Failed to get all member lists" but this endpoint retrieves RSVPs.
if (!response || !response.Items) {
throw new DatabaseFetchError({
- message: "Failed to get all member lists.",
+ message: "Failed to fetch RSVPs for event.",
});
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (!response || !response.Items) { | |
| throw new DatabaseFetchError({ | |
| message: "Failed to get all member lists.", | |
| }); | |
| } | |
| if (!response || !response.Items) { | |
| throw new DatabaseFetchError({ | |
| message: "Failed to fetch RSVPs for event.", | |
| }); | |
| } |
🤖 Prompt for AI Agents
In src/api/routes/rsvp.ts around lines 112 to 116, the thrown DatabaseFetchError
uses a misleading message "Failed to get all member lists" even though this
endpoint retrieves RSVPs; update the error message to accurately reflect the
operation (e.g., "Failed to get all RSVPs" or "Failed to fetch RSVPs") while
keeping the same error type and payload structure so callers and logs report the
correct failure context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (3)
src/api/routes/rsvp.ts (3)
38-43: Fixz.jwt()toz.string().jwt().
z.jwt()is not a valid Zod v4 method. This will cause a runtime error. Usez.string().jwt()instead.headers: z.object({ - "x-uiuc-token": z.jwt().min(1).meta({ + "x-uiuc-token": z.string().jwt().min(1).meta({ description: "An access token for the user in the UIUC Entra ID tenant.", }), }),
112-116: Fix misleading error message.The error message says "Failed to get all member lists" but this endpoint retrieves RSVPs.
if (!response || !response.Items) { throw new DatabaseFetchError({ - message: "Failed to get all member lists.", + message: "Failed to fetch RSVPs for event.", }); }
68-74: SetcreatedAtto an actual timestamp.The
createdAtfield is set to an empty string, which defeats its purpose. This should be a proper ISO timestamp for auditing and sorting.const entry = { partitionKey: `${request.params.eventId}#${upn}`, eventId: request.params.eventId, userId: upn, isPaidMember, - createdAt: "", + createdAt: new Date().toISOString(), };
🧹 Nitpick comments (1)
src/api/routes/rsvp.ts (1)
52-52: Remove unused destructured variables.
givenNameandsurnameare destructured but never used in the handler. Either remove them or use them (e.g., for logging or storing in the RSVP entry).- const { userPrincipalName: upn, givenName, surname } = verifiedData; + const { userPrincipalName: upn } = verifiedData;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
📒 Files selected for processing (2)
src/api/routes/rsvp.ts(1 hunks)src/common/roles.ts(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/api/routes/rsvp.ts (5)
src/api/components/index.ts (2)
withTags(243-258)withRoles(194-241)src/api/functions/uin.ts (1)
verifyUiucAccessToken(30-96)src/common/errors/index.ts (2)
ValidationError(99-108)DatabaseFetchError(121-130)src/api/functions/membership.ts (1)
checkPaidMembership(359-400)src/common/config.ts (1)
genericConfig(246-246)
🪛 ESLint
src/api/routes/rsvp.ts
[error] 1-1: Resolve error: EACCES: permission denied, open '/jZkjPRImBn'
at Object.writeFileSync (node:fs:2409:20)
at l (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:13685)
at createFilesMatcher (/home/jailuser/git/node_modules/get-tsconfig/dist/index.cjs:7:14437)
at Object.resolve (/home/jailuser/git/node_modules/eslint-import-resolver-typescript/lib/index.cjs:298:107)
at withResolver (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:180:23)
at fullResolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:201:22)
at relative (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:217:10)
at resolve (/home/jailuser/git/node_modules/eslint-module-utils/resolve.js:233:12)
at checkFileExtension (/home/jailuser/git/node_modules/eslint-plugin-import/lib/rules/extensions.js:205:53)
at checkSourceValue (/home/jailuser/git/node_modules/eslint-module-utils/moduleVisitor.js:32:5)
(import/extensions)
[error] 2-2: Unexpected use of file extension "js" for "api/plugins/rateLimiter.js"
(import/extensions)
[error] 3-3: Unexpected use of file extension "js" for "api/components/index.js"
(import/extensions)
[error] 11-11: Unexpected use of file extension "js" for "common/errors/index.js"
(import/extensions)
[error] 13-13: Unexpected use of file extension "js" for "api/functions/uin.js"
(import/extensions)
[error] 14-14: Unexpected use of file extension "js" for "api/functions/membership.js"
(import/extensions)
[error] 16-16: Unexpected use of file extension "js" for "common/config.js"
(import/extensions)
[error] 17-17: Unexpected use of file extension "js" for "common/roles.js"
(import/extensions)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build Application
- GitHub Check: Run Unit Tests
🔇 Additional comments (6)
src/common/roles.ts (2)
10-11: LGTM!The new roles follow the existing naming convention and are properly added to the enum.
52-53: LGTM!Human-readable mappings are correctly added, addressing the previous review comment about missing entries in
AppRoleHumanMapper.src/api/routes/rsvp.ts (4)
1-17: Imports look correct.The
.jsextensions flagged by ESLint are a standard pattern for TypeScript projects using ES modules with Node.js resolution. TheUnauthorizedErrorimport, while not directly used in this file, is available for future use if needed.
19-24: Rate limiter configuration looks appropriate.30 requests per 30 seconds is reasonable for RSVP operations.
83-100: Authorization properly implemented.The GET endpoint correctly uses
withRoles([AppRoles.VIEW_RSVPS], ...)andonRequest: fastify.authorizeFromSchemato restrict access.
117-122: Deduplication logic is correct.Using
Mapto deduplicate byuserIdand spreadingvalues()back into an array is an idiomatic approach.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.